#ifndef __INTERRUPT_H
#define __INTERRUPT_H
#include "matrix.h"
#include "mxsetup.h"
#include "dlist.h"
#include "overload.h"

/* the number of interrupts the system can support */
#define MX_INTERRUPT_NUM INTERRUPT_NUM

/**
 * function [ mx_interrupt_disable ] implementation
 * @prototype: void mx_interrupt_disable (void);
 *      @param: void
 *      @return: void
 * @desc: disable all of the interrupts
 */
extern void mx_interrupt_disable (void);

/**
 * function [ mx_interrupt_enable ] implementation
 * @prototype: void mx_interrupt_enable (void);
 *      @param: void
 *      @return: void
 * @desc: enable all of the interrupts
 */
extern void mx_interrupt_enable (void);

/**
 * function [ mx_interrupt_disable_nest ] implementation
 * @prototype: mx_usize_t mx_interrupt_disable_nest (void);
 *      @param: void
 *      @return: the interrupt state before closing the interrupt
 * @desc: disable all of the interrupts and return the interrupt state before closing the interrupt
 */
extern mx_usize_t mx_interrupt_disable_nest (void);

/**
 * function [ mx_interrupt_enable_nest ] implementation
 * @prototype: void mx_interrupt_enable_nest (mx_usize_t state);
 *      @param state: the interrupt state that the mx_interrupt_disable return
 *      @return: void
 * @desc: recover the interrupt state that before called mx_interrupt_disable
 */
extern void mx_interrupt_enable_nest (mx_usize_t state);

/* interrupt handler definition */
typedef void (*mx_interrupt_handler_t) (mx_void_pt param);

/**
 * mount the interrupt handler to system
 * @prototype: mx_interrupt_pt mx_interrupt_register(mx_usize_t intno, mx_interrupt_handler_t handler);
 * @prototype: mx_interrupt_pt mx_interrupt_register(mx_usize_t intno, mx_interrupt_handler_t handler, mx_void_pt param);
 * @prototype: mx_interrupt_pt mx_interrupt_register(mx_string_pt name, mx_usize_t intno, mx_interrupt_handler_t handler, mx_void_pt param);
 * @note: it's a package for `mx_interrupt_mount`
 */
#define mx_interrupt_register(...) OVERLOAD(__mx_interrupt_register, __VA_ARGS__)
#define __mx_interrupt_register1(intno, handler, p2, p3, p4, p5, p6, p7) ({  \
    static mx_interrupt_t _i_intr;  \
    mx_interrupt_mount(&_i_intr, "int"#intno, intno, handler, mx_null); \
    &_i_intr;   \
})
#define __mx_interrupt_register2(intno, handler, param, p3, p4, p5, p6, p7) ({  \
    static mx_interrupt_t _i_intr;  \
    mx_interrupt_mount(&_i_intr, "int"#intno, intno, handler, param); \
    &_i_intr;   \
})
#define __mx_interrupt_register3(name, intno, handler, param, p4, p5, p6, p7) ({  \
    static mx_interrupt_t _i_intr;  \
    mx_interrupt_mount(&_i_intr, name, intno, handler, param); \
    &_i_intr;   \
})

/**
 * interrupt struct definition
 */
typedef struct
{
    volatile mx_bool_t suspend;  // the mark record whether the interrupt has been suspend (the hardware interrupt still work)
    mx_string_pt name;
    mx_void_pt param;
    mx_dlist_pt hlist;  // hook function list
    mx_interrupt_handler_t handler;
} mx_interrupt_t, *mx_interrupt_pt;

/**
 * hook struct definition
 */
typedef struct
{
    mx_dlist_t dlist;
    mx_usize_t intno;                   // the interrupt number which it is hooked, used by uhook operation
    mx_interrupt_handler_t handler;     // hook function
} mx_interrupt_hook_t, *mx_interrupt_hook_pt;

/* mark the interrupt already entried */
void mx_interrupt_enter (mx_void_pt context);
/* mark the interrupt already left, clear all flags */
void mx_interrupt_leave (void);
/* check if it is currently interrupted */
mx_bool_t mx_interrupt_isentered (void);
/* mount the interrupt handler to system */
void mx_interrupt_mount (mx_interrupt_pt pinterrupt, mx_string_pt name,
                         mx_usize_t intno, mx_interrupt_handler_t handler, mx_void_pt param);
/* unmount the interrupt handler */
void mx_interrupt_umount (mx_usize_t intno);
/* hook a existing interrupt */
void mx_interrupt_hook (mx_interrupt_hook_pt phook, mx_usize_t intno, mx_interrupt_handler_t handler);
/* unhook a existing interrupt */
void mx_interrupt_uhook (mx_interrupt_hook_pt phook);
/* suspend a existing interrupt, the hardware interrupt still work */
void mx_interrupt_suspend (mx_usize_t intno);
/* resume a existing interrupt from suspend to active */
void mx_interrupt_resume (mx_usize_t intno);
/* handle the interrupt by interrupt number, called by the hardware interrupt */
void mx_interrupt_handle (mx_usize_t intno);

#endif
